/******************************
 * @Developer: many
 * @File: hook-filerotatelogs.go
 * @Time: 2022/5/13 11:20
******************************/

package hooks

import (
	"fmt"
	"github.com/sirupsen/logrus"
	"gopkg.in/ini.v1"
	"io"
	"logger/fixopt"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type splitter struct {
	nt             int64
	FileDescriptor *os.File

	Prefix       string `ini:"prefix"`
	FilePath     string `ini:"filepath"`
	RotationTime int64  `ini:"rotationtime"`
}

func (self *splitter) Ini(x *ini.File) error {
	return x.Section("log").MapTo(self)
}

func (self *splitter) Close() error {
	if self.FileDescriptor == nil {
		return nil
	}
	return self.FileDescriptor.Close()
}

func (self *splitter) Next() (name string, err error) {
	now := time.Now().Unix()
	if self.nt == 0 {
		self.nt = (now/self.RotationTime)*self.RotationTime - self.RotationTime
	}

	if self.nt+self.RotationTime > now {
		return "", nil
	}

	self.nt += self.RotationTime
	if err = self.Close(); err != nil {
		return "", err
	}

	name = self.Name()
	power := os.O_CREATE | os.O_RDWR | os.O_APPEND
	self.FileDescriptor, err = os.OpenFile(filepath.Join(self.FilePath, name), power, 0766)

	return name, err
}

func (self *splitter) Name() string {
	return fmt.Sprintf("%s[%s].log", self.Prefix, time.Unix(self.nt, 0).Format("200601021504"))
}

type ChkFileHook struct {
	file       splitter
	writemutex sync.Mutex

	aborignal struct {
		once   sync.Once
		writer io.Writer
	}
}

func (self *ChkFileHook) Levels() []logrus.Level {
	return logrus.AllLevels
}

func (self *ChkFileHook) Fire(entry *logrus.Entry) error {
	self.writemutex.Lock()
	defer self.writemutex.Unlock()

	self.aborignal.once.Do(func() {
		self.aborignal.writer = entry.Logger.Out
	})

	if name, err := self.file.Next(); err != nil {
		return err
	} else if name != "" {
		if self.aborignal.writer == nil {
			entry.Logger.Out = self.file.FileDescriptor
		} else {
			entry.Logger.Out = io.MultiWriter(self.aborignal.writer, self.file.FileDescriptor)
		}

	}

	return nil
}

func init() {
	hookinstaller = append(hookinstaller, func(opt ...*fixopt.Field) (logrus.Hook, error) {
		file := new(splitter)
		if err := fixopt.FixOptions(file, opt...); err != nil {
			return nil, err
		}

		if file.RotationTime <= 0 {
			return nil, nil
		}

		file.RotationTime *= 60
		return &ChkFileHook{file: *file}, nil
	})
}
